package org.easy.eao.spring.hibernate;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.easy.eao.annotations.Named;
import org.easy.eao.annotations.Paging;
import org.easy.eao.exception.ActionInitializeException;
import org.easy.eao.exception.ReturnTypeException;
import org.easy.eao.support.Page;
import org.easy.eao.utils.Parameter;
import org.easy.eao.utils.ReflectUtils;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;

/**
 * 分页存取动作实现类
 * 
 * @author Frank
 */
public class PagingAction extends HibernateActionSupport<Paging> {

	private static final String DEFAULT_NAMEDQUERY_SUFFIX = "[count]";
	private static final String HQL_REGEX = "[f|F][r|R][o|O][m|M] ";

	/**
	 * 分页查询信息类
	 * 
	 * @author Frank
	 */
	private class PagingQueryInfo {
		/** 默认页码 */
		private int defaultPage;
		/** 默认分页容量 */
		private int defaultSize;
		/** 是否使用命名查询 */
		private boolean named;
		/** 查询语句标识(HQL/查询名) */
		private String findQuery;
		/** 统计语句标识(HQL/查询名) */
		private String countQuery;
	}

	/**
	 * 分页查询对象
	 * 
	 * @author Frank
	 */
	private class PagingQuery {
		private Query find;
		private Query count;
		private int page;
		private int size;
	}

	/** 页码索引值 */
	private Integer pageIdx;
	/** 页容量索引值 */
	private Integer sizeIdx;
	/** 位置参数 */
	private List<Parameter> indexParams = new ArrayList<Parameter>(0);
	/** 命名参数 */
	private List<Parameter> namedParams = new ArrayList<Parameter>(0);
	/** 分页查询信息 */
	private PagingQueryInfo queryInfo = new PagingQueryInfo();

	// 初始化的方法

	@Override
	public void initialize(Annotation annotation, Method method, Class<?> clz) {
		super.initialize(annotation, method, clz);
		if (!Page.class.isAssignableFrom(method.getReturnType()))
			throw new ReturnTypeException("返回类型必须是 Page 类型");
		initizlizePagingQueryInfo();
		initizlizeParameters();
		if (pageIdx == null && queryInfo.defaultPage == 0)
			throw new ActionInitializeException("缺少页码参数设置");
		if (sizeIdx == null && queryInfo.defaultSize == 0)
			throw new ActionInitializeException("缺少分页容量参数设置");
	}

	/**
	 * 初始化分页查询信息
	 */
	private void initizlizePagingQueryInfo() {
		// 获取默认分页数据
		queryInfo.defaultPage = this.annotation.page();
		queryInfo.defaultSize = this.annotation.size();
		// 获取查询设置
		org.easy.eao.annotations.Query query = this.annotation.query();
		if (StringUtils.isBlank(query.named())) {
			queryInfo.findQuery = query.query();
		} else {
			queryInfo.findQuery = query.named();
			queryInfo.named = true;
		}
		// 获取统计设置
		org.easy.eao.annotations.Query count = this.annotation.count();
		if (queryInfo.named) {
			if (StringUtils.isBlank(count.named()))
				queryInfo.countQuery = queryInfo.findQuery + DEFAULT_NAMEDQUERY_SUFFIX;
			else
				queryInfo.countQuery = count.named();
		} else {
			if (StringUtils.isBlank(count.query()))
				queryInfo.countQuery = getDefaultCountHQL(queryInfo.findQuery);
			else
				queryInfo.countQuery = count.query();
		}
	}

	/**
	 * 获取默认统计语句
	 * 
	 * @param query
	 * @return
	 */

	private String getDefaultCountHQL(String query) {
		String[] s = query.split(HQL_REGEX);
		return "select count(*) from " + s[1];
	}

	/**
	 * 初始化查询参数设置
	 * 
	 * @param method
	 */
	private void initizlizeParameters() {
		Parameter[] parameters = ReflectUtils.getParameter(method);
		for (Parameter p : parameters) {
			if (p.hasAnnotation(Paging.Number.class)) {
				pageIdx = p.getIndex();
				continue;
			}
			if (p.hasAnnotation(Paging.Size.class)) {
				sizeIdx = p.getIndex();
				continue;
			}
			if (p.hasAnnotation(Named.class)) {
				namedParams.add(p);
				continue;
			}
			indexParams.add(p);
		}
	}

	// 执行时的方法

	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object execute(final Object[] parameters) {
		return getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				PagingQuery query = createPagingQuery(session, parameters);
				fittingQuery(query, parameters);
				return executeQuery(query, method);
			}
		});
	}

	/**
	 * 创建分页查询对象
	 * 
	 * @param session
	 *            当前的 Session
	 * @return
	 */
	private PagingQuery createPagingQuery(Session session, Object[] parameters) {
		PagingQuery result = new PagingQuery();
		// 获取查询对象
		if (queryInfo.named) {
			result.find = session.getNamedQuery(queryInfo.findQuery);
			result.count = session.getNamedQuery(queryInfo.countQuery);
		} else {
			result.find = session.createQuery(queryInfo.findQuery);
			result.count = session.createQuery(queryInfo.countQuery);
		}
		// 获取分页实际值
		if (pageIdx != null)
			result.page = parameters[pageIdx] == null ? 
					queryInfo.defaultPage : (Integer) parameters[pageIdx];
		else
			result.page = queryInfo.defaultPage;
		if (sizeIdx != null)
			result.size = parameters[sizeIdx] == null ?
					queryInfo.defaultSize : (Integer) parameters[sizeIdx];
		else
			result.size = queryInfo.defaultSize;
		return result;
	}

	/**
	 * 设置查询参数
	 * 
	 * @param query
	 * @param parameters
	 */
	protected void fittingQuery(PagingQuery query, Object[] parameters) {
		for (int i = 0; i < indexParams.size(); i++) {
			Parameter p = indexParams.get(i);
			query.find.setParameter(i, parameters[p.getIndex()]);
			query.count.setParameter(i, parameters[p.getIndex()]);
		}
		for (Parameter p : namedParams) {
			Named named = p.getAnnotation(Named.class);
			setParameter(query, parameters[p.getIndex()], named.value());
		}
		query.find.setFirstResult(Page.getStartOfPage(query.page, query.size));
		query.find.setMaxResults(query.size);
	}

	/**
	 * 设置命名参数
	 * 
	 * @param query
	 * 			     分页查询对象
	 * @param para
	 *            参数值
	 * @param named
	 *            参数名
	 */
	@SuppressWarnings("rawtypes")
	private void setParameter(PagingQuery query, Object para, String named) {
		Class<?> clz = para.getClass();
		if (clz.isArray()) {
			query.find.setParameterList(named, (Object[]) para);
			query.count.setParameterList(named, (Object[]) para);
			return;
		}
		if (Collection.class.isAssignableFrom(para.getClass())) {
			query.find.setParameterList(named, (Collection) para);
			query.count.setParameterList(named, (Collection) para);
			return;
		}
		query.find.setParameter(named, para);
		query.count.setParameter(named, para);
	}

	/**
	 * 执行查询
	 * 
	 * @param query
	 * @param method
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected Page<?> executeQuery(PagingQuery query, Method method) {
		long total = ((Long) query.count.uniqueResult()).longValue();
		int first = Page.getStartOfPage(query.page, query.size);
		if (total < first)
			return new Page(first, total, query.size, new ArrayList());
		return new Page(first, total, query.size, query.find.list());
	}
}
